using System;
using System.Collections;
using System.Collections.Generic;
using Commons.Preferences;

namespace Commons.Injector
{
	public class ContextParser
	{
		public int NumObjects { get; private set; }
		public int TotalObjects { get; private set; }

		public IEnumerator Parse()
		{
			ContextSectionParser[] sectionParsers = {
				new ContextSectionParser("Data/context")
			};

			foreach (ContextSectionParser sectionParser in sectionParsers)
			{
				sectionParser.Load();

				yield return null;
			}

			foreach (ContextSectionParser sectionParser in sectionParsers)
			{
				TotalObjects += sectionParser.TotalObjects;
			}

			yield return null;

			foreach (ContextSectionParser sectionParser in sectionParsers)
			{
				int initialNum = NumObjects;

				IEnumerator enumerator = sectionParser.Parse();
				while (enumerator.MoveNext())
				{
					NumObjects = initialNum + sectionParser.NumObjects;

					yield return enumerator.Current;
				}
			}

			yield return null;
		}

		private void Validate()
		{
			foreach (ObjectDefinition objDef in Context.Instance.definitions.Values)
			{
				if (objDef.ctorDef != null)
				{
					foreach (CtorParamDefinition param in objDef.ctorDef.parameters.Values)
					{
						if (param.refId != null)
						{
							ObjectDefinition paramObjDef;
							if (Context.Instance.definitions.TryGetValue(param.refId, out paramObjDef))
							{
								if (param.lazy)
								{
									throw new Exception("Object " + param.refId + " should not be in context, its referenced by " + objDef.id + " as lazy");
								}

								string paramScope = paramObjDef.scope;
								if (paramScope != Context.GlobalScope && paramScope != objDef.scope)
								{
									throw new Exception("Object " + objDef.id + " (" + objDef.scope + ") reference object " + param.refId + " (" + paramScope + ") from a different scope");
								}
							}
							else if (!param.lazy)
							{
								throw new Exception("Missing object " + param.refId + " referenced by " + objDef.id);
							}
						}
					}
				}

				foreach (PropertyDefinition propDef in objDef.properties.Values)
				{
					if (propDef.refId != null)
					{
						ObjectDefinition paramObjDef;
						if (Context.Instance.definitions.TryGetValue(propDef.refId, out paramObjDef))
						{
							if (propDef.lazy)
							{
								throw new Exception("Object " + propDef.refId + " should not be in context, its referenced by " + objDef.id + " as lazy");
							}

							string paramScope = paramObjDef.scope;
							if (paramScope != Context.GlobalScope && paramScope != objDef.scope)
							{
								throw new Exception("Object " + objDef.id + " (" + objDef.scope + ") reference object " + propDef.refId + " (" + paramScope + ") from a different scope");
							}
						}
						else if (!propDef.lazy)
						{
							throw new Exception("Missing object " + propDef.refId + " referenced by " + objDef.id);
						}
					}
				}
			}
		}
	}
}
